using System;
using System.Collections.Generic;
using System.Threading;

namespace InfiniTec.Threading
{
	public abstract class OperationAction
    {
		public static OperationAction WaitForEventCompletion()
		{
			return WaitForEventOperation.Default;
		}

		public static OperationAction WaitForAsyncCompletion()
		{
			return WaitForAsyncCompletionOperation.Default;
		}

		public static OperationAction Rethrow(Exception exception)
		{
			if (exception == null) throw new ArgumentNullException("exception");

			return new RethrowOperation(exception);
		}

		public static OperationAction Return<T>(T result)
		{
			return new OperationResult<T>(result);
		}

		public static OperationAction ExecuteBaseOperation()
		{
			return Threading.ExecuteBaseOperation.Default;
		}

		public static OperationAction ReportProgress(object status, int progressPercentage)
		{
			return new OperationProgress(status, progressPercentage);
		}

		public static OperationAction WaitForHandle(WaitHandle handle)
		{
			return new WaitForHandleOperation(handle);
		}

		public static OperationAction WaitForAsyncResultCompletion(IAsyncResult asyncResult)
		{
			return new WaitForAsyncResultOperation(asyncResult);
		}

		public static OperationAction WaitForHandle(WaitHandle handle, object status, int percentComplete)
		{
			return new WaitForHandleOperation(handle, status, percentComplete);
		}

		public static OperationAction Wait(TimeSpan duration)
		{
			return new WaitAction(duration);
		}

		public static OperationAction WaitUntil(DateTime time)
		{
			return new WaitAction(time);
		}

		public static OperationAction ExecuteAsync(params AsyncOperation[] operations)
		{
			return ExecuteAsync((IEnumerable<AsyncOperation>) operations);
		}

		public static OperationAction ExecuteAsync(IEnumerable<AsyncOperation> operations)
		{
			if (operations == null) throw new ArgumentNullException("operations");

			List<NestedOperation> result = new List<NestedOperation>();

			foreach (AsyncOperation operation in operations)
			{
				result.Add(new NestedOperation(operation));				
			}

			if (result.Count == 0)
			{
				throw new ArgumentOutOfRangeException("operations", "At least one operation must be specified.");
			}
			if (result.Count == 1)
			{
				return result[0];
			}

			return new ParallelOperation(result);
		}

		public static OperationAction ExecuteAsync(params NestedOperation[] operations)
		{
			if (operations == null) throw new ArgumentNullException("operations");
			if (operations.Length == 0)
			{
				throw new ArgumentOutOfRangeException("operations", "At least one operation must be specified.");
			}

			return new ParallelOperation(operations);
		}

		public static NestedOperation ExecuteAsync(AsyncOperation operation, params Type[] exceptionsToHandle)
		{
			if (operation == null) throw new ArgumentNullException("operation");
			if (exceptionsToHandle == null) throw new ArgumentNullException("exceptionsToHandle");

			return new NestedOperation(operation, exceptionsToHandle);
		}

	}
}